﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using JScriptSuite.Common;
using JScriptSuite.QA.Lib;
using JScriptSuite.InteropServices;

namespace JScriptSuite.QA.App
{
    class DifferenceRecorder
    {
        readonly StringBuilder differences;
        readonly List<string> path;

        internal DifferenceRecorder()
        {
            differences = new StringBuilder();
            path = new List<string>();
        }

        internal string Differences
        {
            get
            {
                return differences.ToString();
            }
        }

        internal void AddDifferences(ResultItem expected, ResultItem detected)
        {
            if (path == null)
            {
                if (detected != null)
                {
                    AddError("Detected js-tag '{0}'. Expected null insteed js", detected.Tag);
                    return;
                }
            }
            else if (detected == null)
            {
                AddError("Detected js-tag '{0}'. Expected null insteed js", detected.Tag);
            }

            path.Add(expected.Tag);
            if (CompareTags(expected, detected))
            {
                CompareValues(expected.Items, detected.Items);
                CompareChildren(expected.Children, detected.Children);
            }
            path.RemoveAt(path.Count - 1);
        }

        bool CompareTags(ResultItem expected, ResultItem detected)
        {
            return Compare(expected.Tag, detected.Tag, "Mismatch tag.");
        }

        void CompareValues(object[] expected, object[] detected)
        {
            if (Compare(expected.Length, detected.Length, "Mismatch items count."))
            {
                for (int i = 0; i < expected.Length; i++)
                {
                    if (!object.Equals(expected[i], detected[i]))
                    {
                        AddError(string.Format("Item '{0}'", i), expected[i], detected[i]);
                    }
                }
            }
        }

        void CompareChildren(ResultItem[] expected, ResultItem[] detected)
        {
            if (Compare(expected.Length, detected.Length, "Mismatch children count."))
            {
                for (int i = 0; i < expected.Length; i++)
                {
                    AddDifferences(expected[i], detected[i]);
                }
            }
        }

        bool Compare(object expected, object detected, string message)
        {
            if (object.Equals(expected, detected))
            {
                return true;
            }

            AddError(message, expected, detected);
            return false;
        }

        void AddError(string message, object expected, object detected)
        {
            AddError("{0} Expected '{1}'. Detected '{2}'", message, Translate(expected), Translate(detected));
        }

        void AddError(string format, params object[] parameters)
        {
            for (int i = 0; i < path.Count; i++)
            {
                differences.Append(path[i]);
                differences.Append(':');
            }

            differences.AppendFormat(format, parameters);
            differences.AppendLine();
        }

        static object Translate(object source)
        {
            return object.Equals("\0", source) ? "<ZeroString>" : source;
        }
    }
}
